package com.example.mobiledatapackage.controller;

import com.example.mobiledatapackage.domain.Package;
import com.example.mobiledatapackage.domain.User;
import com.example.mobiledatapackage.domain.UsingStatus;
import com.example.mobiledatapackage.domain.WeakSpot;
import com.example.mobiledatapackage.ontology.OntProp;
import com.example.mobiledatapackage.ontology.RdfConvertor;
import com.example.mobiledatapackage.ontology.UriConvertor;
import com.example.mobiledatapackage.request.RecommendReq;
import com.example.mobiledatapackage.request.UserStatus;
import com.example.mobiledatapackage.response.RecommendResp;
import com.example.mobiledatapackage.response.UserDTO;
import com.example.mobiledatapackage.service.PackageService;
import com.example.mobiledatapackage.service.RecommendService;
import com.example.mobiledatapackage.service.UserService;
import com.example.mobiledatapackage.service.UsingStatusService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.jena.rdf.model.*;
import org.apache.jena.reasoner.Reasoner;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/query")
@Slf4j
@Tag(name = "套餐推荐")
public class PackageQueryController {

    @Autowired
    private Model rawModel;

    @Autowired
    private Reasoner reasoner;

    @Autowired
    private PackageService packageService;

    @Autowired
    private RecommendService recommendService;

    @Autowired
    private RdfConvertor rdfConvertor;

    @Autowired
    private UserService userService;

    @Autowired
    private UsingStatusService usingStatusService;

    @GetMapping("/all-pkg")
    @Operation(summary = "获取全部的套餐数据")
    public List<Package> getAllPackages() {
        return packageService.selectAll();
    }


    @PostMapping("/recommend")
    @Transactional
    @Operation(summary = "根据用户信息来进行套餐的推荐")
    public RecommendResp packageRecommend(@RequestBody RecommendReq recommendReq) {
        User curUser = userService.selectByPhone(recommendReq.getPhone());
        UsingStatus usingStatus = usingStatusService.selectLatestRecord(recommendReq.getPhone());
        UserStatus userStatus = UserStatus.valueOf(curUser, usingStatus);
        // 将用户信息转换成 RDF 并存放于一个 Jena model 中
        String userUri = UriConvertor.convertUser(curUser.getUserId());
        Model userStatusModel = rdfConvertor.convertUserStatus(userStatus, curUser.getUserId());
        // 将所有 model 和推理机并在一起，并获得 infModel，它包含了根据规则而推理得到的数据
        var localModel = rawModel.union(userStatusModel);
        InfModel infModel = ModelFactory.createInfModel(reasoner, localModel);
        // 把推理得到的推荐结果提取出来
        Resource user = infModel.getResource(userUri);
        StmtIterator needMoreStmtIter = user.listProperties(OntProp.needMore);  // 我们将所需要对当前套餐进行改进的部分（如流量、短信数）称为 weak spot
        List<WeakSpot> weakSpots = needMoreStmtIter.toList().stream()
                .map(
                    (Statement stmt) -> new WeakSpot(stmt.getPredicate(), stmt.getObject()))
                .collect(Collectors.toList());
        StmtIterator likeAppSeriesStmtIter = user.listProperties(OntProp.likeAppSeries);  // 将用户喜好的 APP 系列收集起来
        weakSpots.addAll(
                likeAppSeriesStmtIter.toList().stream()
                        .map((Statement stmt) -> new WeakSpot(stmt.getPredicate(), stmt.getObject().asResource()))
                        .collect(Collectors.toList())
        );
        // 按照 weak spot 搜索更适合用户的套餐，将搜索结果返回
        var cond = recommendService.makeQueryCondByWeakSpots(userStatus, weakSpots);
        List<Package> recommendPackages = packageService.recommendQueryByCond(cond);
        if (recommendPackages == null || recommendPackages.isEmpty()) {
            cond.setAppSeriesPreferences(null);
            recommendPackages = packageService.recommendQueryByCond(cond);
        }
        // 将所要响应给前端的数据组装起来
        UserDTO userInResp = new UserDTO();
        BeanUtils.copyProperties(curUser, userInResp);
        Package curPackage = packageService.selectByPrimaryKey(userStatus.getPkgId());
        return new RecommendResp(recommendPackages, userInResp, usingStatus, curPackage);
    }
}
